home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / python-support / gnome-games-data / glchess / game.py < prev    next >
Encoding:
Python Source  |  2009-04-14  |  24.9 KB  |  836 lines

  1. # -*- coding: utf-8 -*-
  2. """
  3. """
  4.  
  5. __author__ = 'Robert Ancell <bob27@users.sourceforge.net>'
  6. __license__ = 'GNU General Public License Version 2'
  7. __copyright__ = 'Copyright 2005-2006  Robert Ancell'
  8.  
  9. import chess.board
  10. import chess.san
  11.  
  12. # Game results
  13. RESULT_IN_PROGRESS         = '*'
  14. RESULT_WHITE_WINS          = '1-0'
  15. RESULT_BLACK_WINS          = '0-1'
  16. RESULT_DRAW                = '1/2-1/2'
  17.  
  18. # Reasons for the result
  19. RULE_CHECKMATE             = 'CHECKMATE'
  20. RULE_STALEMATE             = 'STALEMATE'
  21. RULE_TIMEOUT               = 'TIMEOUT'
  22. RULE_FIFTY_MOVES           = 'FIFTY_MOVES'
  23. RULE_THREE_FOLD_REPETITION = 'THREE_FOLD_REPETITION'
  24. RULE_INSUFFICIENT_MATERIAL = 'INSUFFICIENT_MATERIAL'
  25. RULE_RESIGN                = 'RESIGN'
  26. RULE_DEATH                 = 'DEATH'
  27. RULE_AGREEMENT             = 'AGREEMENT'
  28. RULE_ABANDONMENT           = 'ABANDONMENT'
  29.  
  30. class ChessMove:
  31.     """
  32.     """
  33.     # The move number (game starts at 0)
  34.     number     = 0
  35.     
  36.     # The player and piece that moved
  37.     player     = None
  38.     piece      = None
  39.     
  40.     # The piece that was promoted to (or None)
  41.     promotion  = None
  42.     
  43.     # The victim piece (or None)
  44.     victim     = None
  45.  
  46.     # The start and end position of the move
  47.     start      = None
  48.     end        = None
  49.     
  50.     # The move in CAN and SAN format
  51.     canMove    = ''
  52.     sanMove    = ''
  53.  
  54.     # The game result after this move
  55.     opponentInCheck = False
  56.     opponentCanMove = False
  57.     
  58.     # If this move can be used as a resignation
  59.     fiftyMoveRule = False
  60.     threeFoldRepetition = False
  61.     
  62.     # A comment about this move
  63.     comment = ''
  64.  
  65.     # Numeric annotation glyph for move
  66.     nag     = ''
  67.  
  68. class ChessPlayer:
  69.     """
  70.     """
  71.     # The name of the player
  72.     __name = None
  73.     
  74.     # The game this player is in
  75.     __game = None
  76.  
  77.     # Flag to show if this player is able to move
  78.     __readyToMove = False
  79.     
  80.     def __init__(self, name):
  81.         """Constructor for a chess player.
  82.  
  83.         'name' is the name of the player.
  84.         """
  85.         self.__name = str(name)
  86.         self.isAlive = True
  87.  
  88.     # Methods to extend
  89.  
  90.     def onPieceMoved(self, piece, start, end, delete):
  91.         """Called when a chess piece is moved.
  92.         
  93.         'piece' is the piece that has been moved (chess.board.ChessPiece).
  94.         'start' is the location the piece in LAN format (string) or None if the piece has been created.
  95.         'end' is the location the piece has moved to in LAN format (string).
  96.         'delete' is a flag to show if the piece should be deleted when it arrives there (boolean).
  97.         """
  98.         pass
  99.     
  100.     def onPlayerMoved(self, player, move):
  101.         """Called when a player has moved.
  102.         
  103.         'player' is the player that has moved (ChessPlayer).
  104.         'move' is the record for this move (ChessMove).
  105.         """
  106.         pass
  107.     
  108.     def onUndoMove(self):
  109.         pass
  110.     
  111.     def onPlayerStartTurn(self, player):
  112.         pass
  113.  
  114.     def onGameEnded(self, game):
  115.         """Called when a chess game has ended.
  116.         
  117.         'game' is the game that has ended (Game).
  118.         """
  119.         pass
  120.     
  121.     def readyToMove(self):
  122.         """FIXME
  123.         """
  124.         pass
  125.  
  126.     # Public methods
  127.  
  128.     def getName(self):
  129.         """Get the name of this player.
  130.         
  131.         Returns the player name (string).
  132.         """
  133.         return self.__name
  134.     
  135.     def getGame(self):
  136.         """Get the game this player is in.
  137.         
  138.         Returns the game (Game) or None if not in a game.
  139.         """
  140.         return self.__game
  141.     
  142.     def getRemainingTime(self):
  143.         """Get the amount of time this player has remaining.
  144.         
  145.         Returns the amount of time in milliseconds.
  146.         """
  147.         if self is self.__game.getWhite():
  148.             timer = self.__game.whiteTimer
  149.         elif self is self.__game.getBlack():
  150.             timer = self.__game.blackTimer
  151.         else:
  152.             return 0
  153.         
  154.         if timer is None:
  155.             return 0
  156.         else:
  157.             return timer.controller.getRemaining()
  158.  
  159.     def isReadyToMove(self):
  160.         """
  161.         """
  162.         return self.__readyToMove
  163.     
  164.     def canMove(self, start, end, promotionType = chess.board.QUEEN):
  165.         """
  166.         """
  167.         return self.__game.canMove(self, start, end, promotionType)
  168.  
  169.     def move(self, move):
  170.         """Move a piece.
  171.         
  172.         'move' is the move to make in Normal/Long/Standard Algebraic format (string).
  173.         """
  174.         self.__game.move(self, move)
  175.         
  176.     def undo(self):
  177.         """Undo moves until it is this players turn"""
  178.         self.__game.undo(self)
  179.         
  180.     def endMove(self):
  181.         """Complete this players turn"""
  182.         self.__game.endMove(self)
  183.         
  184.     def resign(self):
  185.         """Resign from the game"""
  186.         self.__game.resign(self)
  187.         
  188.     def claimDraw(self):
  189.         """Claim a draw"""
  190.         return self.__game.claimDraw()
  191.  
  192.     def outOfTime(self):
  193.         """Report this players timer has expired"""
  194.         self.__game.outOfTime(self)
  195.         
  196.     def die(self):
  197.         """Report this player has died"""
  198.         self.isAlive = False
  199.         if self.__game is not None:
  200.             self.__game.killPlayer(self)
  201.  
  202.     # Private methods
  203.     
  204.     def _setGame(self, game):
  205.         """
  206.         """
  207.         self.__game = game
  208.         
  209.     def _setReadyToMove(self, readyToMove):
  210.         if self.__readyToMove == readyToMove:
  211.             return
  212.         self.__readyToMove = readyToMove
  213.         if readyToMove is True:
  214.             self.readyToMove()
  215.  
  216. class ChessGameBoard(chess.board.ChessBoard):
  217.     """
  218.     """
  219.     
  220.     # Reference to the game
  221.     __game = None
  222.     
  223.     def __init__(self, game):
  224.         """
  225.         """
  226.         self.__game = game
  227.         chess.board.ChessBoard.__init__(self)
  228.  
  229.     def onPieceMoved(self, piece, start, end, delete):
  230.         """Called by chess.board.ChessBoard"""
  231.         self.__game._onPieceMoved(piece, start, end, delete)
  232.  
  233. class ChessGameSANConverter(chess.san.SANConverter):
  234.     """
  235.     """
  236.         
  237.     __colourToSAN = {chess.board.WHITE: chess.san.SANConverter.WHITE,
  238.                      chess.board.BLACK: chess.san.SANConverter.BLACK}
  239.     __sanToColour = {}
  240.     for (a, b) in __colourToSAN.iteritems():
  241.         __sanToColour[b] = a
  242.         
  243.     __typeToSAN = {chess.board.PAWN:   chess.san.SANConverter.PAWN,
  244.                    chess.board.KNIGHT: chess.san.SANConverter.KNIGHT,
  245.                    chess.board.BISHOP: chess.san.SANConverter.BISHOP,
  246.                    chess.board.ROOK:   chess.san.SANConverter.ROOK,
  247.                    chess.board.QUEEN:  chess.san.SANConverter.QUEEN,
  248.                    chess.board.KING:   chess.san.SANConverter.KING}
  249.     __sanToType = {}
  250.     for (a, b) in __typeToSAN.iteritems():
  251.         __sanToType[b] = a
  252.         
  253.     def __init__(self, board, moveNumber):
  254.         self.board = board
  255.         self.moveNumber = moveNumber
  256.         chess.san.SANConverter.__init__(self)
  257.     
  258.     def decode(self, colour, move):
  259.         (start, end, result, promotionType) = chess.san.SANConverter.decode(self, self.__colourToSAN[colour], move)
  260.         return (start, end, self.__sanToType[promotionType])
  261.     
  262.     def encode(self, start, end, isTake, promotionType):
  263.         if promotionType is None:
  264.             promotion = self.QUEEN
  265.         else:
  266.             promotion = self.__typeToSAN[promotionType]
  267.         return chess.san.SANConverter.encode(self, start, end, isTake, promotion)
  268.  
  269.     def getPiece(self, location):
  270.         """Called by chess.san.SANConverter"""
  271.         piece = self.board.getPiece(location, self.moveNumber)
  272.         if piece is None:
  273.             return None
  274.         return (self.__colourToSAN[piece.getColour()], self.__typeToSAN[piece.getType()])
  275.     
  276.     def testMove(self, colour, start, end, promotionType, allowSuicide = False):
  277.         """Called by chess.san.SANConverter"""
  278.         move = self.board.testMove(self.__sanToColour[colour], start, end,
  279.                                      self.__sanToType[promotionType], allowSuicide, self.moveNumber)
  280.         if move is None:
  281.             return False
  282.  
  283.         if move.opponentInCheck:
  284.             if not move.opponentCanMove:
  285.                 return chess.san.SANConverter.CHECKMATE
  286.             return chess.san.SANConverter.CHECK
  287.         return True
  288.  
  289. class ChessGame:
  290.     """
  291.     """    
  292.     # The players and spectators in the game
  293.     __players = None
  294.     __whitePlayer = None
  295.     __blackPlayer = None
  296.     __spectators = None
  297.     
  298.     # The board to move on
  299.     board = None
  300.     
  301.     # The game state (started and player to move)
  302.     __started = False
  303.     __currentPlayer = None
  304.     
  305.     __moves = None
  306.     
  307.     __inCallback = False
  308.     __queuedCalls = None
  309.     
  310.     result  = RESULT_IN_PROGRESS
  311.     rule    = None
  312.     
  313.     whiteTimer = None
  314.     blackTimer = None
  315.     
  316.     def __init__(self):
  317.         """Game constructor"""
  318.         self.__players = []
  319.         self.__spectators = []
  320.         self.board = ChessGameBoard(self)
  321.         self.__moves = []
  322.         self.__queuedCalls = []
  323.         
  324.     def getAlivePieces(self, moveNumber = -1):
  325.         """Get the alive pieces on the board.
  326.         
  327.         'moveNumber' is the move to get the pieces from (integer).
  328.         
  329.         Returns a dictionary of the alive pieces (board.ChessPiece) keyed by location.
  330.         Raises an IndexError exception if moveNumber is invalid.
  331.         """
  332.         return self.board.getAlivePieces(moveNumber)
  333.     
  334.     def getDeadPieces(self, moveNumber = -1):
  335.         """Get the dead pieces from the game.
  336.         
  337.         'moveNumber' is the move to get the pieces from (integer).
  338.         
  339.         Returns a list of the pieces (board.ChessPiece) in the order they were killed.
  340.         Raises an IndexError exception if moveNumber is invalid.
  341.         """
  342.         return self.board.getDeadPieces(moveNumber)
  343.     
  344.     def setTimers(self, whiteTimer, blackTimer):
  345.         """
  346.         """
  347.         self.whiteTimer = whiteTimer
  348.         self.blackTimer = blackTimer
  349.  
  350.     def setWhite(self, player):
  351.         """Set the white player in the game.
  352.         
  353.         'player' is the player to use as white.
  354.         
  355.         If the game has started or there is a white player an exception is thrown.
  356.         """
  357.         assert(self.__started is False)
  358.         assert(self.__whitePlayer is None)
  359.         self.__whitePlayer = player
  360.         self.__connectPlayer(player)
  361.  
  362.     def getWhite(self):
  363.         """Returns the current white player (player.Player)"""
  364.         return self.__whitePlayer
  365.     
  366.     def setBlack(self, player):
  367.         """Set the black player in the game.
  368.         
  369.         'player' is the player to use as black.
  370.         
  371.         If the game has started or there is a black player an exception is thrown.
  372.         """
  373.         assert(self.__started is False)
  374.         assert(self.__blackPlayer is None)
  375.         self.__blackPlayer = player
  376.         self.__connectPlayer(player)
  377.         
  378.     def getBlack(self):
  379.         """Returns the current white player (player.Player)"""
  380.         return self.__blackPlayer
  381.     
  382.     def getCurrentPlayer(self):
  383.         """Get the player to move"""
  384.         return self.__currentPlayer
  385.     
  386.     def addSpectator(self, player):
  387.         """Add a spectator to the game.
  388.         
  389.         'player' is the player spectating.
  390.         
  391.         This can be called after the game has started.
  392.         """
  393.         self.__spectators.append(player)
  394.         self.__connectPlayer(player)
  395.  
  396.     def isStarted(self):
  397.         """Returns True if the game has been started"""
  398.         return self.__started
  399.         
  400.     def start(self, moves = []):
  401.         """Start the game.
  402.         
  403.         'moves' is a list of moves to start with.
  404.         
  405.         If there is no white or black player then an exception is raised.
  406.         """
  407.         assert(self.__whitePlayer is not None and self.__blackPlayer is not None)
  408.         
  409.         # Disabled for now
  410.         #import network
  411.         #self.x = network.GameReporter('Test Game', 12345)
  412.         #print 'Reporting'
  413.  
  414.         # Load starting moves
  415.         self.__currentPlayer = self.__whitePlayer
  416.         for move in moves:
  417.             self.move(self.__currentPlayer, move)
  418.             if self.__currentPlayer is self.__whitePlayer:
  419.                 self.__currentPlayer = self.__blackPlayer
  420.             else:
  421.                 self.__currentPlayer = self.__whitePlayer
  422.  
  423.         self.__started = True
  424.  
  425.         # Stop if both players aren't alive
  426.         if not self.__whitePlayer.isAlive:
  427.             self.killPlayer(self.__whitePlayer)
  428.             return
  429.         if not self.__blackPlayer.isAlive:
  430.             self.killPlayer(self.__blackPlayer)
  431.             return
  432.  
  433.         # Stop if game ended on loaded moves
  434.         if self.result != RESULT_IN_PROGRESS:
  435.             self._notifyEndGame()
  436.             return
  437.  
  438.         self.startLock()
  439.         
  440.         # Inform other players of the result
  441.         for player in self.__players:
  442.             player.onPlayerStartTurn(self.__currentPlayer)
  443.  
  444.         # Get the next player to move
  445.         self.__currentPlayer._setReadyToMove(True)
  446.  
  447.         self.endLock()
  448.  
  449.     def getSquareOwner(self, coord):
  450.         """TODO
  451.         """
  452.         piece = self.board.getPiece(coord)
  453.         if piece is None:
  454.             return None
  455.         
  456.         colour = piece.getColour()
  457.         if colour is chess.board.WHITE:
  458.             return self.__whitePlayer
  459.         elif colour is chess.board.BLACK:
  460.             return self.__blackPlayer
  461.         else:
  462.             return None
  463.         
  464.     def canMove(self, player, start, end, promotionType):
  465.         """Test if a player can move.
  466.         
  467.         'player' is the player making the move.
  468.         'start' is the location to move from in LAN format (string).
  469.         'end' is the location to move from in LAN format (string).
  470.         'promotionType' is the piece type to promote pawns to. FIXME: Make this a property of the player
  471.         
  472.         Return True if can move, otherwise False.
  473.         """
  474.         if player is not self.__currentPlayer:
  475.             return False
  476.         
  477.         if player is self.__whitePlayer:
  478.             colour = chess.board.WHITE
  479.         elif player is self.__blackPlayer:
  480.             colour = chess.board.BLACK
  481.         else:
  482.             assert(False)
  483.  
  484.         move = self.board.testMove(colour, start, end, promotionType = promotionType)
  485.  
  486.         return move is not None
  487.     
  488.     def move(self, player, move):
  489.         """Get a player to make a move.
  490.         
  491.         'player' is the player making the move.
  492.         'move' is the move to make in SAN or LAN format (string).
  493.         """
  494.         if self.__inCallback:
  495.             self.__queuedCalls.append((self.move, (player, move)))
  496.             return
  497.         
  498.         self.startLock()
  499.         
  500.         if player is not self.__currentPlayer:
  501.             print 'Player attempted to move out of turn'
  502.         else:
  503.             self._move(player, move)
  504.  
  505.         self.endLock()
  506.  
  507.     def undo(self, player):
  508.         if self.__inCallback:
  509.             self.__queuedCalls.append((self.undo, (player,)))
  510.             return
  511.         
  512.         self.startLock()
  513.         
  514.         self.__whitePlayer._setReadyToMove(False)
  515.         self.__blackPlayer._setReadyToMove(False)
  516.         
  517.         # Pretend the current player is the oponent so when endMove() is called
  518.         # this player will become the active player.
  519.         # Yes, this IS a big hack...
  520.         if player is self.__whitePlayer:
  521.             self.__currentPlayer = self.__blackPlayer
  522.         else:
  523.             self.__currentPlayer = self.__whitePlayer
  524.         
  525.         # If this player hasn't moved then undo oponents move before their one
  526.         if len(self.__moves) > 0 and self.__moves[-1].player is not player:
  527.             count = 2
  528.         else:
  529.             count = 1
  530.             
  531.         for i in xrange(count):
  532.             if len(self.__moves) != 0:
  533.                 self.board.undo()
  534.                 self.__moves = self.__moves[:-1]
  535.                 for p in self.__players:
  536.                     p.onUndoMove()
  537.         
  538.         self.endLock()
  539.         
  540.     def startLock(self):
  541.         assert(self.__inCallback is False)
  542.         self.__inCallback = True
  543.         
  544.     def endLock(self):
  545.         self.__inCallback = False
  546.         while len(self.__queuedCalls) > 0:
  547.             (call, args) = self.__queuedCalls[0]
  548.             self.__queuedCalls = self.__queuedCalls[1:]
  549.             call(*args)
  550.  
  551.     def _move(self, player, move):
  552.         """
  553.         """
  554.         if self.result != RESULT_IN_PROGRESS:
  555.             print 'Game completed'
  556.             return
  557.         
  558.         if self.__currentPlayer is self.__whitePlayer:
  559.             colour = chess.board.WHITE
  560.         else:
  561.             colour = chess.board.BLACK
  562.  
  563.         # If move is SAN process it as such
  564.         try:
  565.             (start, end, _, _, promotionType, _) = chess.lan.decode(colour, move)
  566.         except chess.lan.DecodeError, e:
  567.             converter = ChessGameSANConverter(self.board, len(self.__moves))
  568.             try:
  569.                 (start, end, promotionType) = converter.decode(colour, move)
  570.             except chess.san.Error, e:
  571.                 print 'Invalid move: ' + move
  572.                 return
  573.  
  574.         # Only use promotion type if a pawn move to far file
  575.         piece = self.board.getPiece(start)
  576.         promotion = None
  577.         if piece is not None and piece.getType() is chess.board.PAWN:
  578.             if colour is chess.board.WHITE:
  579.                 if end[1] == '8':
  580.                     promotion = promotionType
  581.             else:
  582.                 if end[1] == '1':
  583.                     promotion = promotionType
  584.  
  585.         moveResult = self.board.movePiece(colour, start, end, promotionType)
  586.         if moveResult is None:
  587.             print 'Illegal move: ' + str(move)
  588.             return
  589.         
  590.         # Re-encode for storing and reporting
  591.         canMove = chess.lan.encode(colour, start, end, promotionType = promotion)
  592.         converter = ChessGameSANConverter(self.board, len(self.__moves))
  593.         try:
  594.             sanMove = converter.encode(start, end, moveResult.victim != None, promotionType)
  595.         except chess.san.Error:
  596.             # If for some reason we couldn't make the SAN move the use the CAN move instead
  597.             sanMove = canMove
  598.  
  599.         m = ChessMove()
  600.         if len(self.__moves) == 0:
  601.             m.number = 1
  602.         else:
  603.             m.number = self.__moves[-1].number + 1
  604.         m.player              = self.__currentPlayer
  605.         m.piece               = piece
  606.         m.victim              = moveResult.victim
  607.         m.start               = start
  608.         m.end                 = end
  609.         m.canMove             = canMove
  610.         m.sanMove             = sanMove
  611.         m.opponentInCheck     = moveResult.opponentInCheck
  612.         m.opponentCanMove     = moveResult.opponentCanMove
  613.         m.fiftyMoveRule       = moveResult.fiftyMoveRule
  614.         m.threeFoldRepetition = moveResult.threeFoldRepetition
  615.         #FIXME: m.comment             = move.comment
  616.         #FIXME: m.nag                 = move.nag
  617.  
  618.         self.__moves.append(m)
  619.  
  620.         # This player has now moved
  621.         self.__currentPlayer._setReadyToMove(False)
  622.  
  623.         # Inform other players of the result
  624.         for player in self.__players:
  625.             player.onPlayerMoved(self.__currentPlayer, m)
  626.  
  627.         # Check if the game has ended
  628.         result = RESULT_IN_PROGRESS
  629.         if not m.opponentCanMove:
  630.             if self.__currentPlayer is self.__whitePlayer:
  631.                 result = RESULT_WHITE_WINS
  632.             else:
  633.                 result = RESULT_BLACK_WINS
  634.             if m.opponentInCheck:
  635.                 rule = RULE_CHECKMATE
  636.             else:
  637.                 result = RESULT_DRAW
  638.                 rule = RULE_STALEMATE
  639.  
  640.         # Check able to complete
  641.         if not self.board.sufficientMaterial():
  642.             result = RESULT_DRAW
  643.             rule = RULE_INSUFFICIENT_MATERIAL
  644.  
  645.         if result is not RESULT_IN_PROGRESS:
  646.             self.endGame(result, rule)
  647.  
  648.     def endMove(self, player):
  649.         """
  650.         """
  651.         if self.__inCallback:
  652.             self.__queuedCalls.append((self.endMove, (player,)))
  653.             return
  654.         
  655.         if player is not self.__currentPlayer:
  656.             print 'Player attempted to move out of turn'
  657.             return
  658.         if player.move is None:
  659.             print "Ending move when haven't made one"
  660.             return
  661.  
  662.         if self.__currentPlayer is self.__whitePlayer:
  663.             self.__currentPlayer = self.__blackPlayer
  664.         else:
  665.             self.__currentPlayer = self.__whitePlayer
  666.         
  667.         self.startLock()
  668.         
  669.         # Inform other players of the result
  670.         for player in self.__players:
  671.             player.onPlayerStartTurn(self.__currentPlayer)
  672.  
  673.         # Notify the next player they can move
  674.         if self.__started is True and self.result == RESULT_IN_PROGRESS:
  675.             self.__currentPlayer._setReadyToMove(True)
  676.  
  677.         self.endLock()
  678.  
  679.     def resign(self, player):
  680.         """Get a player to resign.
  681.         
  682.         'player' is the player resigning.
  683.         """
  684.         rule = RULE_RESIGN
  685.         if player is self.__whitePlayer:
  686.             self.endGame(RESULT_BLACK_WINS, rule)
  687.         else:
  688.             self.endGame(RESULT_WHITE_WINS, rule)
  689.             
  690.     def claimDraw(self):
  691.         """
  692.         """
  693.         # TODO: Penalise if make an incorrect attempt
  694.         try:
  695.             move = self.__moves[-1]
  696.         except IndexError:
  697.             return False
  698.         else:
  699.             if move.fiftyMoveRule:
  700.                 rule = RULE_FIFTY_MOVES
  701.             elif move.threeFoldRepetition:
  702.                 rule = RULE_THREE_FOLD_REPETITION
  703.             else:
  704.                 return False
  705.  
  706.         self.endGame(RESULT_DRAW, rule)
  707.         return True
  708.  
  709.     def killPlayer(self, player):
  710.         """Report a player has died
  711.         
  712.         'player' is the player that has died.
  713.         """
  714.         if player is self.__whitePlayer:
  715.             result = RESULT_BLACK_WINS
  716.         elif player is self.__blackPlayer:
  717.             result = RESULT_WHITE_WINS       
  718.         self.endGame(result, RULE_DEATH)
  719.  
  720.     def outOfTime(self, player):
  721.         """Report a player's timer has expired"""
  722.         if player is self.__whitePlayer:
  723.             result = RESULT_BLACK_WINS
  724.         elif player is self.__blackPlayer:
  725.             result = RESULT_WHITE_WINS
  726.         else:
  727.             assert(False)
  728.         self.endGame(result, RULE_TIMEOUT)
  729.         
  730.     def abandon(self):
  731.         self.endGame(RESULT_DRAW, RULE_ABANDONMENT)
  732.  
  733.     def endGame(self, result, rule):
  734.         if self.result != RESULT_IN_PROGRESS:
  735.             return
  736.         self.result = result
  737.         self.rule = rule
  738.         if self.isStarted():
  739.             self._notifyEndGame()
  740.  
  741.     def _notifyEndGame(self):
  742.         self.__currentPlayer._setReadyToMove(False)
  743.         for player in self.__players:
  744.             player.onGameEnded(self)
  745.  
  746.     def getMoves(self):
  747.         """
  748.         """
  749.         return self.__moves
  750.  
  751.     def abort(self):
  752.         """End the game"""
  753.         # Inform players
  754.         for player in self.__players:
  755.             player.onGameEnded(self)
  756.  
  757.     # Private methods:
  758.  
  759.     def __connectPlayer(self, player):
  760.         """Add a player into the game.
  761.         
  762.         'player' is the player to add.
  763.         
  764.         The player will be notified of the current state of the board.
  765.         """
  766.         self.__players.append(player)
  767.         player._setGame(self)
  768.         
  769.         # Notify the player of the current state
  770.         # FIXME: Make the board iteratable...
  771.         for file in '12345678':
  772.             for rank in 'abcdefgh':
  773.                 coord = rank + file
  774.                 piece = self.board.getPiece(coord)
  775.                 if piece is None:
  776.                     continue
  777.  
  778.                 # These are moves from nowhere to their current location
  779.                 player.onPieceMoved(piece, None, coord, False)
  780.  
  781.     def _onPieceMoved(self, piece, start, end, delete):
  782.         """Called by the chess board"""
  783.         
  784.         # Notify all players of creations and deletions
  785.         # NOTE: Normal moves are done above since the SAN moves are calculated before the move...
  786.         # FIXME: Change this so the SAN moves are done afterwards...
  787.         for player in self.__players:
  788.             player.onPieceMoved(piece, start, end, delete)
  789.  
  790. class NetworkChessGame(ChessGame):
  791.     """
  792.     """
  793.     
  794.     def move(self, player, move):
  795.         """Get a player to make a move.
  796.         
  797.         'player' is the player making the move.
  798.         'move' is the move to make. It can be of the form:
  799.                A coordinate move in the form ((file0, rank0), (file1, rank1), promotionType) ((int, int), (int, int), chess.board.PIECE_TYPE) or
  800.                A SAN move (string).
  801.         """
  802.         # Send to the server
  803.         
  804.             
  805. if __name__ == '__main__':
  806.     game = ChessGame()
  807.     
  808.     import pgn
  809.     
  810.     p = pgn.PGN('black.pgn')
  811.     g = p.getGame(0)
  812.  
  813.     class PGNPlayer(ChessPlayer):
  814.         __moveNumber = 1
  815.         
  816.         __isWhite = True
  817.         
  818.         def __init__(self, isWhite):
  819.             self.__isWhite = isWhite
  820.         
  821.         def readyToMove(self):
  822.             if self.__isWhite:
  823.                 move = g.getWhiteMove(self.__moveNumber)
  824.             else:
  825.                 move = g.getBlackMove(self.__moveNumber)
  826.             self.__moveNumber += 1
  827.             self.move(move)
  828.             
  829.     white = PGNPlayer(True)
  830.     black = PGNPlayer(False)
  831.     
  832.     game.setWhite(white)
  833.     game.setBlack(black)
  834.     
  835.     game.start()
  836.